!> author: 左志华
!> date: 2022-06-08
!>
!> Swift filesystem is not swift. <br>
!> Fortran 文件系统函数拓展
module swift_file_m

    implicit none
    private

    public :: is_exist, file_name, mkdir, rmdir, remove, &
              is_windows, pwd, rename, countlines, windows_path, operator(.join.)

    interface operator(.join.)
        procedure :: join_path
    end interface

contains

    !> Inquire whether a file/directory exists <br>
    !> 查询文件 / 文件夹路径是否存在
    impure elemental logical function is_exist(file_name, dir)
        character(*), intent(in) :: file_name   !! file name <br>
                                                !! 文件 / 文件夹名称
        logical, intent(in), optional :: dir    !! is directory ? <br>
                                                !! 是否为文件夹 ?
#if defined __INTEL_COMPILER
        if (present(dir)) then
            if (dir) then
                inquire (directory=file_name, exist=is_exist)
                return
            end if
        end if
#endif
        inquire (file=file_name, exist=is_exist)

    end function is_exist

    !> Make directory <br>
    !> 生成文件夹路径
    impure elemental subroutine mkdir(path)
        character(*), intent(in) :: path    !! path name <br>
                                            !! 文件夹路径
        integer :: exitstat
        if (is_windows()) then
            call execute_command_line("md "//windows_path(path), exitstat=exitstat)
        else
            call execute_command_line("mkdir -p "//path, exitstat=exitstat)
        end if

    end subroutine mkdir

    !> Remove directory <br>
    !> 删除文件夹路径
    impure elemental subroutine rmdir(path)
        character(*), intent(in) :: path    !! path name <br>
                                            !! 文件夹路径
        integer :: exitstat
        if (is_windows()) then
            call execute_command_line("rd /s /q "//windows_path(path), exitstat=exitstat)
        else
            call execute_command_line("rm -rf "//path, exitstat=exitstat)
        end if

    end subroutine rmdir

    !> Remove file <br>
    !> 删除文件
    impure elemental subroutine remove(file_name)
        character(*), intent(in) :: file_name   !! file name <br>
                                                !! 文件名称
        integer :: tmp_unit, istat

        open (newunit=tmp_unit, file=file_name, status='old', iostat=istat)
        if (istat == 0) close (tmp_unit, status='delete')

    end subroutine remove

    !> Judge if the system is Windows operating system <br>
    !> 判断系统是否为 Windows 操作系统
    impure logical function is_windows()
        character(16) :: os_name
        logical, save :: is_windows_ = .false.
        logical, save :: is_first_run = .true.

        if (is_first_run) then
            call get_environment_variable("OS", os_name) ! GNU extension, IFORT extension
            is_windows_ = trim(os_name) == "Windows_NT"
            is_first_run = .false.
            is_windows = is_windows_
        else
            is_windows = is_windows_
        end if

    end function is_windows

    !> Get current working directory <br>
    !> 获取当前工作路径
    impure function pwd()
        character(:), allocatable :: pwd
        character(256) :: pwd_

        call getcwd(pwd_) ! GNU extension, IFORT extension
        pwd = trim(pwd_)

    end function pwd

    ! gfortran has an rename intrinsic function, but here gived the another.
    !> Move file or rename file <br>
    !> 移动文件或者重命名文件
    impure subroutine rename(old_file, new_file)
        character(*), intent(in) :: old_file    !! old file name <br>
                                                !! 旧文件名称
        character(*), intent(in) :: new_file    !! new file name <br>
                                                !! 新文件名称
        integer :: exitstat

        if (is_windows()) then
            call execute_command_line('ren "'//windows_path(old_file)//'" "'// &
                                      new_file(scan(new_file, "/\", .true.) + 1:)//'"', &
                                      exitstat=exitstat)
        else
            call execute_command_line("mv "//old_file//" "//new_file, exitstat=exitstat)
        end if

    end subroutine rename

    !> Counts the line number of text file <br>
    !> 计算文本文件行数
    impure integer function countlines(file_name)
        character(*), intent(in) :: file_name   !! file name <br>
                                                !! 文件名称
        integer :: istat, iunit

        open (newunit=iunit, file=file_name, status='old')
        countlines = 0
        do
            read (iunit, *, iostat=istat)
            if (is_iostat_end(istat)) exit
            countlines = countlines + 1
        end do
        close (iunit)

    end function countlines

    !> Replace file system separator for Windows <br>
    !> 将文件系统分隔符替换为 Windows 的分隔符
    pure function windows_path(path) result(winpath)
        character(*), intent(in) :: path    !! path name <br>
                                            !! 文件路径
        character(len(path)) :: winpath
        integer :: ik

        do concurrent(ik=1:len(path))
            if (path(ik:ik) == "/") then
                winpath(ik:ik) = "\"
            else
                winpath(ik:ik) = path(ik:ik)
            end if
        end do

    end function windows_path

    !> Return the file name of the file unit <br>
    !> 返回文件单元的文件名称
    impure function file_name(unit)
        integer, intent(in) :: unit             !! file unit <br>
                                                !! 文件单元
        character(:), allocatable :: file_name  !! file name <br>
                                                !! 文件名称
        character(128) :: file_name_

        inquire (unit=unit, name=file_name_)
        file_name = trim(file_name_)

    end function file_name

    !> Merge the directory and file name into a complete path <br>
    !> 将路径中的目录和文件名合并成一个完整的路径
    impure function join_path(dir, file) result(path)
        character(*), intent(in) :: dir             !! Directory name <br>
                                                    !! 目录名
        character(*), intent(in) :: file            !! File name <br>
                                                    !! 文件名
        character(len(dir) + len(file) + 1) :: path !! Complete path <br>
                                                    !! 完整路径

        logical, save :: is_first_run = .true.
        character(1), save :: sep = "/"

        if (is_first_run) then
            if (is_windows()) sep = "\"
            is_first_run = .false.
        end if

        if (dir == "") then
            path = file
        else
            path = dir//sep//file
        end if

    end function join_path

end module swift_file_m
